libobs_wrapper\data\output/
replay_buffer.rs

1//! Provides functionality for working with OBS replay buffers.
2//!
3//! This module extends the ObsOutputRef to provide replay buffer capabilities.
4//! A replay buffer is a special type of output that continuously records
5//! the last N seconds of content, allowing the user to save this buffer on demand. This must be configured. More documentation soon.
6use std::{
7    ffi::c_char,
8    mem::MaybeUninit,
9    path::{Path, PathBuf},
10};
11
12use libobs::calldata_t;
13
14use crate::{
15    run_with_obs,
16    utils::{ObsError, ObsString},
17};
18
19use super::ObsOutputRef;
20
21/// Defines functionality specific to replay buffer outputs.
22///
23/// This trait provides methods for working with replay buffers in OBS,
24/// which are special outputs that continuously record content and allow
25/// on-demand saving of recent footage.
26pub trait ReplayBufferOutput {
27    /// Saves the current replay buffer content to disk.
28    ///
29    /// This method triggers the replay buffer to save its content to a file
30    /// and returns the path to the saved file.
31    ///
32    /// # Returns
33    /// * `Result<Box<Path>, ObsError>` - On success, returns the path to the saved
34    ///   replay file. On failure, returns an error describing what went wrong.
35    fn save_buffer(&self) -> Result<Box<Path>, ObsError>;
36}
37
38/// Implementation of the ReplayBufferOutput trait for ObsOutputRef.
39///
40/// This implementation allows any ObsOutputRef configured as a replay buffer
41/// to save its content to disk via a simple API call.
42impl ReplayBufferOutput for ObsOutputRef {
43    /// Saves the current replay buffer content to disk.
44    ///
45    /// # Implementation Details
46    /// This method:
47    /// 1. Accesses the OBS procedure handler for the output
48    /// 2. Calls the "save" procedure to trigger saving the replay
49    /// 3. Calls the "get_last_replay" procedure to retrieve the saved file path
50    /// 4. Extracts the path string from the calldata and returns it
51    ///
52    /// # Returns
53    /// * `Ok(Box<Path>)` - The path to the saved replay file
54    /// * `Err(ObsError)` - Various errors that might occur during the saving process:
55    ///   - Failure to get procedure handler
56    ///   - Failure to call "save" procedure
57    ///   - Failure to call "get_last_replay" procedure
58    ///   - Failure to extract the path from calldata
59    fn save_buffer(&self) -> Result<Box<Path>, ObsError> {
60        let output_ptr = self.output.clone();
61
62        let path = run_with_obs!(self.runtime, (output_ptr), move || {
63            let ph = unsafe { libobs::obs_output_get_proc_handler(output_ptr) };
64            if ph.is_null() {
65                return Err(ObsError::OutputSaveBufferFailure(
66                    "Failed to get proc handler.".to_string(),
67                ));
68            }
69
70            let name = ObsString::new("save");
71            let call_success = unsafe {
72                let mut calldata = MaybeUninit::<calldata_t>::zeroed();
73                libobs::proc_handler_call(ph, name.as_ptr().0, calldata.as_mut_ptr())
74            };
75
76            if !call_success {
77                return Err(ObsError::OutputSaveBufferFailure(
78                    "Failed to call proc handler.".to_string(),
79                ));
80            }
81
82            let func_get = ObsString::new("get_last_replay");
83            let last_replay = unsafe {
84                let mut calldata = MaybeUninit::<calldata_t>::zeroed();
85                let success =
86                    libobs::proc_handler_call(ph, func_get.as_ptr().0, calldata.as_mut_ptr());
87
88                if !success {
89                    return Err(ObsError::OutputSaveBufferFailure(
90                        "Failed to call get_last_replay.".to_string(),
91                    ));
92                }
93
94                calldata.assume_init()
95            };
96
97            let path_get = ObsString::new("path");
98
99            let mut s = MaybeUninit::<*const c_char>::uninit();
100
101            let res = unsafe {
102                libobs::calldata_get_string(&last_replay, path_get.as_ptr().0, s.as_mut_ptr())
103            };
104            if !res {
105                return Err(ObsError::OutputSaveBufferFailure(
106                    "Failed to get path from last replay.".to_string(),
107                ));
108            }
109
110            let s: *const c_char = unsafe { s.assume_init() };
111            let path = unsafe { std::ffi::CStr::from_ptr(s) }.to_str().unwrap();
112
113            Ok(PathBuf::from(path))
114        })??;
115
116        Ok(path.into_boxed_path())
117    }
118}